
//
// 201210 - AH - (Midtun) Created.
//

package org.soekrat.engine.socraton;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.soekrat.engine.Matcher;

/**
 * <p>
 *   Defines a query for
 *   {@link Socraton}s in a
 *   {@link Socratons} pool.
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC)
 */
public class SocratonQuery
{
  private Matcher m_matcher=null;
  
  private Object m_id;
  
  private final Map<String, Set<String>> m_bisons=
      new HashMap<String, Set<String>>();
  
  /**
   * <p>
   *   Constructor.
   * </p>
   */
  public SocratonQuery(){}
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param matcher {@link Matcher}.
   */
  public SocratonQuery(Matcher matcher)
  {
    if (matcher==null)
    {
      throw new NullPointerException("matcher");
    }
    
    m_matcher=matcher;
  }
  
  /**
   * <p>
   *   Sets 
   *   {@link Matcher} to use.
   * </p>
   * @param matcher {@link Matcher} to use, {@code null} if not to use any.
   * @return {@code this}.
   */
  public SocratonQuery setMatcher(Matcher matcher)
  { 
    m_matcher=matcher; 
    return this; 
  }
  
  /**
   * <p>
   *   Gets
   *   {@link Matcher} used.
   * </p>
   * @return {@link Matcher} or {@code null} if uses none.
   */
  public Matcher getMatcher(){ return m_matcher; }
  
  /**
   * <p>
   *   Clear this query.
   * </p>
   * @return {@code this}.  
   */
  public SocratonQuery clear(){ m_id=null; m_bisons.clear(); return this; }
  
  /**
   * <p>
   *   Sets id of 
   *   {@link Socraton} to match.
   * </p>
   * @param id Id or {@code null} if to clear.
   * @return {@code this}.
   */
  public SocratonQuery id(Object id){ m_id=id; return this; }
  
  /**
   * <p>
   *   Gets id of 
   *   Socraton to match.
   * </>
   * @return Id or {@code null} if not sat.
   */
  public Object id(){ return m_id; }
  
  /**
   * <p>
   *   Adds bison to be matched.
   * </p>
   * @param ctx  Context 
   *             {@link Bison} is in.
   * @param data {@link Bison}'s 
   *             {@link Bison#getLocation()}.
   * @return {@code this}.
   */
  public SocratonQuery bison(String ctx, String data)
  {
    if (ctx==null)
    {
      throw new NullPointerException("ctx");
    }
    if (data==null)
    {
      throw new NullPointerException("data");
    }
    
    Set<String> set=m_bisons.get(ctx);
    if (set==null)
    {
      set=new HashSet<String>();
      m_bisons.put(ctx, set);
    }
    set.add(data);
    
    return this;
  }
  
  /**
   * <p>
   *   Gets names of contexts this match
   *   {@link Bison} in.
   * </p>
   * @return Names.
   */
  public List<String> getContexts()
  {
    return new ArrayList<String>(m_bisons.keySet());
  }
  
  /**
   * <p>
   *   Clear to match in named context. 
   * </p>
   * @param ctx Context name.
   * @return {@code this}.
   */
  public SocratonQuery clearContext(String ctx)
  {
    if (ctx==null)
    {
      throw new NullPointerException("ctx");
    }
    
    m_bisons.remove(ctx);
    return this;
  }
  
  /**
   * <p>
   *   Gets bison data matched in a named context.
   * </p>
   * @param ctx Name on context.
   * @return Data.
   */
  public List<String> getContext(String ctx)
  {
    if (ctx==null)
    {
      throw new NullPointerException("ctx");
    }
    
    List<String> retVal=new ArrayList<String>();
    Collection<String> c=m_bisons.get(ctx);
    retVal.addAll(c);
    return retVal;
  }
  
  /**
   * <p>
   *   Match 
   *   {@link Socraton}.
   * </p>
   * @param socraton {@link Socraton} to match.
   * @return {@code true} if match, {@code false} if does not match.
   */
  boolean match(Socraton socraton)
  {
    if (socraton==null)
    {
      throw new NullPointerException("socraton");
    }
    
    if (m_id!=null)
    {
      if (m_matcher!=null)
      {
        String tid=m_id.toString();
        String oid=socraton.getId().toString();
        if (!m_matcher.match(tid, oid)) return false;
      }
    }
    
    for (Map.Entry<String, Set<String>> e : m_bisons.entrySet())
    {
      String ctx=e.getKey();
      Set<String> set=e.getValue();
      for (String b : set) 
        if (!socraton.contains(ctx, b, m_matcher)) return false;
    }
    
    return true;
  }
  
}
